package com.zidongxiangxi.sharding.job.core.storage;

import com.zidongxiangxi.sharding.job.core.callback.LeaderExecutionCallback;
import com.zidongxiangxi.sharding.job.core.exception.RegExceptionHandler;
import com.zidongxiangxi.sharding.job.core.node.AbstractNode;
import com.zidongxiangxi.sharding.job.core.registry.ZookeeperRegistryCenter;
import org.apache.curator.framework.recipes.leader.LeaderLatch;

import java.util.List;

/**
 * 节点存储
 *
 * @author chenxudong
 * @date 2021/02/18
 */
public class NodeStorage {
    private final String jobName;
    private final ZookeeperRegistryCenter regCenter;

    public NodeStorage(final String jobName, final ZookeeperRegistryCenter regCenter) {
        this.jobName = jobName;
        this.regCenter = regCenter;
    }

    /**
     * 判断任务节点是否存在
     *
     * @param node 节点
     * @return 节点是否存在
     */
    public boolean isJobNodeExisted(final String node) {
        return regCenter.isExisted(AbstractNode.getFullPath(jobName, node));
    }

    /**
     * 判断任务根节点是否存在
     *
     * @return 任务根节点是否存在
     */
    public boolean isJobRootNodeExisted() {
        return regCenter.isExisted("/" + jobName);
    }

    /**
     * 获取任务节点数据
     *
     * @param node 节点
     * @return 节点数据
     */
    public String getJobNodeData(final String node) {
        return regCenter.get(AbstractNode.getFullPath(jobName, node));
    }

    /**
     * 从注册中心直接获取节点数据，不结果缓存
     *
     * @param node 节点
     * @return 节点数据
     */
    public String getJobNodeDataDirectly(final String node) {
        return regCenter.getDirectly(AbstractNode.getFullPath(jobName, node));
    }

    /**
     * 获取节点的子节点列表
     *
     * @param node 节点
     * @return 子节点列表
     */
    public List<String> getJobNodeChildrenKeys(final String node) {
        return regCenter.getChildrenKeys(AbstractNode.getFullPath(jobName, node));
    }

    /**
     * 如果节点不存在，则创建节点
     *
     * @param node 节点
     */
    public void createJobNodeIfNeeded(final String node) {
        if (isJobRootNodeExisted() && !isJobNodeExisted(node)) {
            regCenter.persist(AbstractNode.getFullPath(jobName, node), "");
        }
    }

    /**
     * 如果节点存在，删除节点
     *
     * @param node 节点
     */
    public void removeJobNodeIfExisted(final String node) {
        if (isJobNodeExisted(node)) {
            regCenter.remove(AbstractNode.getFullPath(jobName, node));
        }
    }

    /**
     * 填充任务节点
     *
     * @param node 节点
     * @param value 节点数据
     */
    public void fillJobNode(final String node, final Object value) {
        regCenter.persist(AbstractNode.getFullPath(jobName, node), value.toString());
    }

    /**
     * 填充临时的任务节点
     *
     * @param node 节点
     * @param value 节点数据
     */
    public void fillEphemeralJobNode(final String node, final Object value) {
        regCenter.persistEphemeral(AbstractNode.getFullPath(jobName, node), value.toString());
    }

    /**
     * 更新任务节点
     *
     * @param node 节点
     * @param value 新的节点数据
     */
    public void updateJobNode(final String node, final Object value) {
        regCenter.update(AbstractNode.getFullPath(jobName, node), value.toString());
    }

    /**
     * 代替任务节点
     *
     * @param node 节点
     * @param value 要替换的数据
     */
    public void replaceJobNode(final String node, final Object value) {
        regCenter.persist(AbstractNode.getFullPath(jobName, node), value.toString());
    }

    /**
     * 代替任务根节点
     *
     * @param value 要替换的数据
     */
    public void replaceJobRootNode(final Object value) {
        regCenter.persist("/" + jobName, value.toString());
    }

    /**
     * 选举主节点
     *
     * @param latchNode 选举主节点使用的锁
     * @param callback 选举完成后的回调
     */
    public void executeInLeader(final String latchNode, final LeaderExecutionCallback callback) {
        try (LeaderLatch latch = new LeaderLatch(regCenter.getClient(), AbstractNode.getFullPath(jobName, latchNode))) {
            latch.start();
            latch.await();
            callback.execute();
            //CHECKSTYLE:OFF
        } catch (final Exception ex) {
            //CHECKSTYLE:ON
            RegExceptionHandler.handleException(ex);
        }
    }

}
